﻿using System.Linq;

namespace Nomo.TcpModule
{
    /// <summary>
    /// 在数据前面存在以 4 个字节表示的真实数据长度的解包器。
    /// </summary>
    public class LengthPrefixUnpacker : IUnpacker
    {
        private       int    _bufferLength;
        private       byte[] _buffer;
        private       int?   _bodyLength;
        private       int    _pointer;
        private const int    HeaderLength    = 4;
        private const int    MinBufferLength = 1 << 5;
        private const int    MaxBufferLength = 1 << 16;

        /// <summary>
        /// 使用默认的缓存大小初始此类型解包器的新实例。
        /// </summary>
        public LengthPrefixUnpacker() : this(1 << 10) { }

        /// <summary>
        /// 使用指定的缓存大小初始此类型解包器的新实例。
        /// </summary>
        /// <param name="bufferLength">缓存大小。</param>
        /// <exception cref="System.NotSupportedException">缓存大小太小了。</exception>
        public LengthPrefixUnpacker(int bufferLength)
        {
            if (bufferLength < MinBufferLength)
            {
                throw new System.NotSupportedException("容量太小。");
            }
            _bufferLength = bufferLength;
            _buffer       = new byte[bufferLength];
            _bodyLength   = null;
            _pointer      = 0;
        }

        void IUnpacker.Init()
        {
            System.Array.Clear(_buffer, 0, _bufferLength);
            _bodyLength = null;
            _pointer    = 0;
            CoreModule.Log.Info($"解包器初始化完成，缓存容量 : {_bufferLength / (1 << 10)} KB .");
        }

        void IUnpacker.Unpack(in System.Collections.Generic.List<byte[]> bytesList)
        {
            if (bytesList == null || bytesList.Count == 0)
            {
                return;
            }

            // 数据总长
            var length = bytesList.Sum(b => b?.Length ?? 0);
            if (length + _pointer > _bufferLength)
            {
                ExpandBufferLength(length + _pointer);
            }
            // 合并所有数据为一个字节数组
            byte[] bytes;
            using (var stream = new CoreModule.UtilityStream(length))
            {
                foreach (var element in bytesList.Where(e => e != null))
                {
                    stream.Write(element, 0, element.Length);
                }
                bytes = stream.ToArray();
            }

            bytesList.Clear();

            for (var i = 0; i < length; i++)
            {
                var b = bytes[i];
                _buffer[_pointer] = b;

                if (_bodyLength == null)
                {
                    // 可以读到真实数据的长度了
                    if (_pointer == HeaderLength - 1)
                    {
                        _bodyLength = System.Net.IPAddress.NetworkToHostOrder(System.BitConverter.ToInt32(_buffer, 0));

                        if (_bodyLength == 0)
                        {
                            // 添加一个空包
                            bytesList.Add(new byte[0]);
                            _bodyLength = null;
                        }
                        else
                        {
                            if (i + (int) _bodyLength > _bufferLength)
                            {
                                ExpandBufferLength(i + (int) _bodyLength);
                            }

                            System.Array.Clear(_buffer, 0, HeaderLength);
                        }

                        _pointer = 0;
                    }
                    // 读不到真实数据的长度，继续读
                    else
                    {
                        _pointer++;
                    }
                }
                else
                {
                    // 可以读完真实数据了
                    if (_pointer == _bodyLength - 1)
                    {
                        // 添加一个正常包
                        var package = new byte[(int) _bodyLength];
                        System.Array.Copy(_buffer, 0, package, 0, (int) _bodyLength);
                        bytesList.Add(package);
                        System.Array.Clear(_buffer, 0, (int) _bodyLength);
                        _bodyLength = null;
                        _pointer    = 0;
                    }
                    // 读不完真实数据，继续读
                    else
                    {
                        _pointer++;
                    }
                }
            }
        }

        /// <summary>
        /// 扩大 <see cref="_buffer"/> 的容量。
        /// </summary>
        /// <param name="atLeast">至少需要的容量。</param>
        private void ExpandBufferLength(int atLeast)
        {
            if (atLeast >= MaxBufferLength)
            {
                throw new System.Exception("解包器的容量不足，并且无法再继续扩大容量。");
            }

            int newBufferLength;
            try
            {
                newBufferLength = CoreModule.NomoMath.NextPow2(atLeast);
            }
            catch (System.OverflowException e)
            {
                System.Console.WriteLine(e);
                throw;
            }

            var newBuffer = new byte[newBufferLength];
            System.Array.Copy(_buffer, 0, newBuffer, 0, _bufferLength);
            _buffer       = newBuffer;
            _bufferLength = newBufferLength;
            CoreModule.Log.Warning("解包器容量不足，已经扩大容量。");
        }
    }
}
